﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace Elvex
{
    /// <summary>
    /// 物体组
    /// </summary>
    public class ItemGroup : MonoBehaviour
    {
        // 元素列表
        public List<GameObject> elements;

        // 物体列表
        public List<GameObject> Items
        {
            get;
        } = new List<GameObject>();

        public bool IsActived { get; private set; } = true;

        private UnityEvent<GameObject> OnDestroyItem { get; } = new UnityEvent<GameObject>();

        public GameObject Add(int elementindex)
        {
            if (elementindex < 0 || elementindex >= elements.Count)
            {
                return null;
            }

            var go = Instantiate(elements[elementindex], transform);
            Items.Add(go);
            go.SetActive(IsActived);
            return go;
        }

        public GameObject Add()
        {
            return Add(0);
        }

        public T Add<T>(int elementindex) where T : Component
        {
            var go = Add(elementindex);
            if(null == go)
            {
                return null;
            }

            go.SetActive(IsActived);
            var component = go.GetComponent<T>();
            if (null == component) component = go.AddComponent<T>();
            return component;
        }

        public T Add<T>() where T : Component
        {
            return Add<T>(0);
        }

        public void AddRange(int num, int elementindex)
        {
            AddRange(num, elementindex, null);
        }

        public void AddRange(int num, int elementindex, UnityAction<int, GameObject> callback)
        {
            CreateRange(num, elementindex, (itemindex, elementidx) =>{
                var go = Add(elementidx);
                callback?.Invoke(itemindex, go);
            });
        }
        
        public void AddRange<T>(int num, int elementindex, UnityAction<int, T> callback) where T : Component
        {
            CreateRange(num, elementindex, (itemindex, elementidx) => {
                var com = Add<T>(elementidx);
                callback?.Invoke(itemindex, com);
            });
        }

        public void AddRange<T>(int num, int elementindex) where T : Component
        {
            AddRange<T>(num, elementindex, null);
        }

        private void CreateRange(int num, int elementindex, UnityAction<int, int> action)
        {
            if (num <= 0)
            {
                return;
            }

            if (elementindex < 0 || elementindex >= elements.Count)
            {
                Debug.LogWarningFormat("ItemGroup num is {0} but elementindex is {1}", elements.Count, elementindex);
                return;
            }

            int startindex = Items.Count;
            for (int i = startindex; i < startindex + num; ++i)
            {
                action?.Invoke(i, elementindex);
            }
        }

        public void Clear()
        {
            for(int i = 0; i < Items.Count; ++i )
            {
                OnDestroyItem?.Invoke(Items[i]);
                Destroy(Items[i].gameObject);
                Items[i] = null;
            }

            Items.Clear();
        }

        public void SetActive(bool value)
        {
            IsActived = value;
            foreach (var item in Items)
            {
                item.gameObject.SetActive(value);
            }
        }

        public T GetItem<T>(int index) where T : Component
        {
            if (index < 0 || index >= Items.Count)
            {
                return null;
            }
            return Items[index].GetComponent<T>();
        }

        public List<T> GetItems<T>() where T : Component
        {
            List<T> list = new List<T>();
            foreach (var item in Items)
            {
                var com = item.GetComponent<T>();
                if (com)
                {
                    list.Add(com);
                }
            }
            return list;
        }

        public GameObject this[int index]
        {
            get { return index < 0 || index >= Items.Count ? null : Items[index]; }
        }

        public int Size { get { return Items.Count; } }
    }
}
